<template>
    <section class="page page--ui-select">
        <h2 class="page__title">UiSelect</h2>

        <p>UiSelect is a select component that allows the user to choose one or more options from a group of pre-defined or dynamically loaded options. It supports default options, multiple selection and filtering.</p>

        <p>UiSelect can show a label above the input, an icon, as well as help or error below the input. It is keyboard accessible and supports a disabled state.</p>

        <h3 class="page__section-title">
            Examples <a href="https://github.com/JosephusPaye/Keen-UI/blob/master/docs-src/pages/UiSelect.vue" target="_blank" rel="noopener">View Source</a>
        </h3>

        <div class="page__examples">
            <h4 class="page__demo-title">Basic</h4>

            <ui-select
                v-model="select1"
                label="Favourite colour"
                placeholder="Select a colour"
                :options="colourStrings"
            ></ui-select>

            <h4 class="page__demo-title">With floating label</h4>

            <ui-select
                v-model="select2"
                floating-label
                label="Favourite colour"
                placeholder="Select a colour"
                :options="colourStrings"
            ></ui-select>

            <h4 class="page__demo-title">With icon</h4>

            <ui-select
                v-model="select2o5"
                icon="colorize"
                label="Favourite colour"
                placeholder="Select a colour"
                :options="colourStrings"
            ></ui-select>

            <h4 class="page__demo-title">With a default selection</h4>

            <ui-select
                v-model="select3"
                label="Favourite colour"
                placeholder="Select a colour"
                :options="colourStrings"
            ></ui-select>

            <h4 class="page__demo-title">Type: image</h4>

            <ui-select
                v-model="select4"
                label="Favourite colour"
                placeholder="Select a colour"
                type="image"
                :options="colours"
            ></ui-select>

            <h4 class="page__demo-title">Type: image, with a default selection</h4>

            <ui-select
                v-model="select5"
                label="Favourite colour"
                placeholder="Select a colour"
                type="image"
                :options="colours"
            ></ui-select>

            <h4 class="page__demo-title">With help</h4>

            <ui-select
                v-model="select6"
                help="You favourite colour will be used on your profile page"
                label="Favourite colour"
                placeholder="Select a colour"
                type="image"
                :options="colours"
            ></ui-select>

            <h4 class="page__demo-title">Multiple selection</h4>

            <ui-select
                v-model="select7"
                label="Favourite colours"
                multiple
                placeholder="Select some colours"
                type="image"
                :options="colours"
            ></ui-select>

            <h4 class="page__demo-title">Multiple selection with defaults</h4>

            <ui-select
                v-model="select8"
                label="Favourite colours"
                multiple
                placeholder="Select some colours"
                type="image"
                :options="colours"
            ></ui-select>

            <h4 class="page__demo-title">With search</h4>

            <ui-select
                v-model="select9"
                has-search
                label="Favourite colours"
                multiple
                placeholder="Select some colours"
                type="image"
                :options="colours"
            ></ui-select>

            <h4 class="page__demo-title">With validation</h4>

            <ui-select
                v-model="select10"
                error="You must select at least 2 colours"
                has-search
                help="Select 2 or more colours"
                label="Favourite colours"
                multiple
                placeholder="Select some colours"

                type="image"
                :invalid="select10Touched && select10.length < 2"

                :options="colours"
                @touch="select10Touched = true"
            ></ui-select>

            <h4 class="page__demo-title">With dynamic options (remote search)</h4>

            <ui-select
                v-model="select11"
                disable-filter
                has-search
                label="Favourite colours"
                placeholder="Select a colour"
                search-placeholder="Type &quot;red&quot; or &quot;blue&quot;"

                type="image"
                :loading="select11Loading"
                :no-results="select11NoResults"

                :options="select11Options"

                @query-change="onQueryChange"
            ></ui-select>

            <h4 class="page__demo-title">With many options (248)</h4>

            <ui-select
                v-model="select12"
                has-search
                label="Country"

                placeholder="Select your country"
                :keys="{ label: 'name', value: 'code' }"

                :options="countries"
            ></ui-select>

            <h4 class="page__demo-title">With custom template</h4>

            <ui-select
                v-model="select12o5"
                has-search
                label="Favourite colours"
                multiple
                placeholder="Select some colours"
                :options="colourStrings"
            >
                <template #option="props">
                    <code>{{ props }}</code>
                </template>
            </ui-select>

            <h4 class="page__demo-title">Disabled</h4>

            <ui-select
                v-model="select13"
                disabled
                label="Favourite colour"
                placeholder="Select a colour"
            ></ui-select>

            <h4 class="page__demo-title">Disabled with a selection</h4>

            <ui-select
                v-model="select14"
                disabled
                label="Favourite colour"
                placeholder="Select a colour"
                :options="colourStrings"
            ></ui-select>
        </div>

        <h3 class="page__section-title">API</h3>

        <ui-tabs raised>
            <ui-tab title="Props">
                <div class="table-responsive">
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Name</th>
                                <th>Type</th>
                                <th>Default</th>
                                <th>Description</th>
                            </tr>
                        </thead>

                        <tbody>
                            <tr>
                                <td>name</td>
                                <td>String</td>
                                <td></td>
                                <td>
                                    <p>The <code>name</code> attribute of the select's hidden input element. Useful when traditionally submitting a form the select is a part of.</p>
                                </td>
                            </tr>

                            <tr>
                                <td class="no-wrap">modelValue, v-model *</td>
                                <td>String, Number, Object or Array</td>
                                <td></td>
                                <td>
                                    <p>The model the selected value or values sync to. Can be set initially for default value/values.</p>
                                    <p>If you are not using <code>v-model</code>, you should listen for the <code>update:modelValue</code> event and update <code>modelValue</code>.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>options</td>
                                <td>Array</td>
                                <td><code>[]</code></td>
                                <td>
                                    <p>An array of options to show to the user.</p>
                                    <p>Can be a plain array, e.g. <code>['Red', 'Blue', 'Green']</code> as well as an array of objects.</p>
                                    <p>For a plain array, the option is shown to the user and it is used for filtering.</p>

                                    <p>For an array of objects, the <code>label</code> is shown to the user and is used for filtering, and the <code>modelValue</code> is submitted to the server. If provided, <code>class</code>, will be applied to the option element's <code>class</code> attribute. You can redefine these keys to fit your data using the <code>keys</code> prop.</p>

                                    <p>The entire option is written to the model when the user makes a selection.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>multiple</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not the user can select multiple options.</p>
                                    <p>Set to <code>true</code> to allow multiple selection.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>multipleDelimiter</td>
                                <td>String</td>
                                <td><code>", "</code></td>
                                <td>The delimiter (separator) to use for displaying multiple selected options.</td>
                            </tr>

                            <tr>
                                <td>placeholder</td>
                                <td>String</td>
                                <td></td>
                                <td>Text to display in the select when no option is selected.</td>
                            </tr>

                            <tr>
                                <td>type</td>
                                <td>String</td>
                                <td><code>"basic"</code></td>
                                <td>
                                    <p>The type of template to use for rendering each option. One of <code>basic</code> or <code>image</code>.</p>
                                    <p>The default type is <code>basic</code>, which simply renders the option text.</p>

                                    <p>The <code>image</code> type renders each option with with an image. To use, set <code>type</code> to <code>image</code> and set an image URL as the <code>image</code> property on each option. You can redefine the <code>image</code> key to fit your data using the <code>keys</code> prop.</p>

                                    <p>You can also render each option with a custom template using the <code>option</code> scoped slot.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>hasSearch</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not to show a search input for filtering the select options or getting a query for remote search.</p>
                                    <p>Set to <code>true</code> to show a search input.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>searchPlaceholder</td>
                                <td>String</td>
                                <td></td>
                                <td>The <code>placeholder</code> attribute of the search input.</td>
                            </tr>

                            <tr>
                                <td>filter</td>
                                <td>Function</td>
                                <td><a href="https://github.com/bevacqua/fuzzysearch" target="_blank" rel="noopener">fuzzysearch</a></td>
                                <td>
                                    <p>Defines a custom filter function that is used for filtering the options when the user types into the search input.</p>

                                    <p>The function is called for each item in the <code>options</code> array with two arguments:</p>
                                    <ul>
                                        <li><code>option</code>: (Number, String or Object) - the current option</li>
                                        <li><code>query</code>: (String) - the current value of the search input (what the user has typed)</li>
                                        <li><code>defaultFilter</code>: (Function) - Keen UI's default filter function. You can call this with the option and query to defer to the default fuzzy filter behavior. Remember to return its result from your custom filter function if you're using it.</li>
                                    </ul>

                                    <p>The function should return <code>true</code> if the option matches the query or <code>false</code> otherwise.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>disableFilter</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not to disable the internal filtering of options. With this set to <code>true</code>, you have to implement filtering externally if needed.</p>
                                    <p>This prop is useful when you want to implement custom/remote search.</p>
                                    <p>See the <b>With dynamic options (remote search)</b> section above for an example usage.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>sort</td>
                                <td>Function</td>
                                <td></td>
                                <td>
                                    <p>Defines a custom sort function that is used for sorting the options after they are filtered.</p>

                                    <p>The function should implement the standard <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#Syntax" target="_blank" rel="noopener"><code>Array.prototype.sort()</code> compare callback</a>.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>loading</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not to show a circular progress spinner on the search input.</p>
                                    <p>This prop is useful for showing feedback to the user when you are doing a remote search. Set to <code>true</code> to show the spinner and <code>false</code> to hide it.</p>
                                    <p>See the <b>With dynamic options (remote search)</b> section above for an example usage.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>noResults</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not the no results message is be shown.</p>
                                    <p>The no results message will be shown if this is <code>true</code> and following conditions are met:</p>
                                    <ul>
                                        <li>The query (user input) is empty</li>
                                        <li>The <code>loading</code> prop is <code>false</code></li>
                                        <li>The <code>disableFilter</code> prop is <code>true</code></li>
                                    </ul>
                                    <p>See the <b>With dynamic options (remote search)</b> section above for an example usage.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>keys</td>
                                <td>Object</td>
                                <td class="no-wrap"><pre class="language-javascript is-compact">{
  class: 'class',
  label: 'label',
  image: 'image'
}</pre></td>
                                <td>
                                    <p>Allows for redefining each option object's keys.</p>
                                    <p>Pass an object with custom keys if your data does not match the default keys.</p>
                                    <p>Note that if you redefine one key, you have to define all the others as well.</p>
                                    <p>Default value can be <a href="https://github.com/JosephusPaye/Keen-UI/blob/master/Customization.md#changing-default-prop-values" target="_blank" rel="noopener">changed globally</a>.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>invalid</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not the select is invalid.</p>
                                    <p>When <code>invalid</code> is <code>true</code>, the select label appears red and the error is shown if available.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>icon</td>
                                <td>String</td>
                                <td></td>
                                <td>
                                    <p>The select icon. Can be any of the <a href="https://design.google.com/icons/" target="_blank" rel="noopener">Material Icons</a>.</p>
                                    <p>You can set a custom or SVG icon using the <code>icon</code> slot.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>iconPosition</td>
                                <td>String</td>
                                <td><code>"left"</code></td>
                                <td>
                                    <p>The position of the icon relative to the select. One of <code>left</code> or <code>right</code>.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>label</td>
                                <td>String</td>
                                <td></td>
                                <td>
                                    <p>The select label (text only). For HTML, use the <code>default</code> slot.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>floatingLabel</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not the label starts out inline and moves to float above the select when it is focused.</p>
                                    <p>Set to <code>true</code> for a floating label. This will disable the select placeholder until the label is floating.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>tabindex</td>
                                <td>Number, String</td>
                                <td></td>
                                <td>The select <code>tabindex</code>.</td>
                            </tr>

                            <tr>
                                <td>help</td>
                                <td>String</td>
                                <td></td>
                                <td>
                                    <p>The help text (hint) shown to the user below the select. For HTML, use the <code>help</code> slot.</p>
                                    <p>Extra space is reserved under the select for the help and error, but if neither is available, this space is collapsed.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>error</td>
                                <td>String</td>
                                <td></td>
                                <td>
                                    <p>The error text shown to the user below the select when the <code>invalid</code> prop is <code>true</code>. For HTML, use the <code>error</code> slot.</p>
                                    <p>Extra space is reserved under the select for the help and error, but if neither is available, this space is collapsed.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>disabled</td>
                                <td>Boolean</td>
                                <td><code>false</code></td>
                                <td>
                                    <p>Whether or not the select is disabled. Set to <code>true</code> to disable the select.</p>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>

                * Required prop
            </ui-tab>

            <ui-tab title="Slots">
                <div class="table-responsive">
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Name</th>
                                <th>Description</th>
                            </tr>
                        </thead>

                        <tbody>
                            <tr>
                                <td>(default)</td>
                                <td>Holds the select label and can contain HTML.</td>
                            </tr>

                            <tr>
                                <td>option</td>
                                <td>
                                    <p>Use this slot to render each option with a custom template. The slot is passed the following properties, which will be available through <code>scope</code>:</p>

                                    <ul>
                                        <li><code>option</code>: (Object or String) - the option</li>
                                        <li><code>index</code>: (Number) - the index of the option in the array of matched options</li>
                                        <li><code>highlighted</code>: (Boolean) - whether or not the option is currently highlighted</li>
                                    </ul>

                                    <p>For more information, see the <a href="https://vuejs.org/v2/guide/components.html#Scoped-Slots">Scoped Slots documentation</a>.</p>
                                </td>
                            </tr>

                            <tr>
                                <td class="no-wrap">no-results</td>
                                <td>
                                    <p>Holds the content shown to the user when there is no matching option for their query.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>icon</td>
                                <td>
                                    <p>Holds the select icon and can contain any custom or SVG icon.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>help</td>
                                <td>
                                    <p>Holds the select help and can contain HTML.</p>
                                </td>
                            </tr>

                            <tr>
                                <td>error</td>
                                <td>
                                    <p>Holds the select error and can contain HTML.</p>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </ui-tab>

            <ui-tab title="Events">
                <table class="table">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Description</th>
                        </tr>
                    </thead>

                    <tbody>
                        <tr>
                            <td>select</td>
                            <td>
                                <p>Emitted when an option is selected. The handler is called with the selected option and an object which shows if the option was selected or deselected.</p>
                                <p>This object will have <code>{ selected: true }</code> if the option was selected or <code>{ selected: false }</code> otherwise.</p>
                                <p>Listen for it using <code>@select</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td class="no-wrap">query-change</td>
                            <td>
                                <p>Emitted when the query (the search input value) changes.</p>
                                <p>The handler is called with the new query. Listen for it using <code>@query-change</code>.</p>
                                <p>See the <b>With dynamic options (remote search)</b> section above for an example usage.</p>
                            </td>
                        </tr>

                        <tr>
                            <td>update:modelValue</td>
                            <td>
                                <p>Emitted when the select value is changed. The handler is called with the new value.</p>
                                <p>If you are not using <code>v-model</code>, you should listen for this event and update the <code>modelValue</code> prop.</p>
                                <p>Listen for it using <code>@update:modelValue</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td>change</td>
                            <td>
                                <p>Emitted when the select value changes.</p>
                                <p>Listen for it using <code>@change</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td>touch</td>
                            <td>
                                <p>Emitted when the select is focused for the first time and then blurred.</p>
                                <p>Listen for it using <code>@touch</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td>focus</td>
                            <td>
                                <p>Emitted when the select is focused.</p>
                                <p>Listen for it using <code>@focus</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td>blur</td>
                            <td>
                                <p>Emitted when the select loses focus.</p>
                                <p>Listen for it using <code>@blur</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td class="no-wrap">dropdown-open</td>
                            <td>
                                <p>Emitted when the select dropdown is opened.</p>
                                <p>Listen for it using <code>@dropdown-open</code>.</p>
                            </td>
                        </tr>

                        <tr>
                            <td class="no-wrap">dropdown-close</td>
                            <td>
                                <p>Emitted when the select dropdown is closed.</p>
                                <p>Listen for it using <code>@dropdown-close</code>.</p>
                            </td>
                        </tr>
                    </tbody>
                </table>
            </ui-tab>

            <ui-tab title="Methods">
                <div class="table-responsive">
                    <table class="table">
                        <thead>
                            <tr>
                                <th>Name</th>
                                <th>Description</th>
                            </tr>
                        </thead>

                        <tbody>
                            <tr>
                                <td><code>focus()</code></td>
                                <td>
                                    <p>Call this method to programmatically focus the select.</p>
                                </td>
                            </tr>

                            <tr>
                                <td class="no-wrap"><code>clearSelection()</code></td>
                                <td>
                                    <p>Call this method to clear the current selection.</p>
                                </td>
                            </tr>

                            <tr>
                                <td class="no-wrap"><code>clearQuery()</code></td>
                                <td>
                                    <p>Call this method to clear the search query.</p>
                                </td>
                            </tr>

                            <tr>
                                <td><code>reset()</code></td>
                                <td>
                                    <p>Call this method to reset the select to its initial value. You should also reset the <code>invalid</code> prop.</p>
                                </td>
                            </tr>

                            <tr>
                                <td class="no-wrap"><code>resetTouched()</code></td>
                                <td>Call this method to reset the touched state of the select. By default it will set the touched state to <code>false</code>, but you can pass an object with <code>{ touched: true }</code> to set the touched state to <code>true</code>.</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </ui-tab>
        </ui-tabs>
    </section>
</template>

<script>
import UiSelect from '@/UiSelect.vue';
import UiTab from '@/UiTab.vue';
import UiTabs from '@/UiTabs.vue';

import debounce from 'lodash.debounce';
import { startsWith } from '@/helpers/util';

import countries from '../data/countries';
import { colourStrings, colours, redShades, blueShades } from '../data/colours';

export default {
    components: {
        UiSelect,
        UiTab,
        UiTabs
    },

    data() {
        return {
            select1: '',
            select2: '',
            select2o5: '',
            select3: 'Orange',
            select4: '',
            select5: {
                label: 'Lavender',
                image: 'https://via.placeholder.com/64/e6e6fa/e6e6fa',
                value: 'lavender'
            },
            select6: '',
            select7: '',
            select8: [
                {
                    label: 'Orange',
                    image: 'https://via.placeholder.com/64/ffa500/ffa500',
                    value: 'orange'
                },
                {
                    label: 'Lime',
                    image: 'https://via.placeholder.com/64/00ff00/00ff00',
                    value: 'lime'
                }
            ],
            select9: [],
            select10: [],
            select10Touched: false,
            select11: '',
            select11Options: [],
            select11Loading: false,
            select11NoResults: false,
            select11LoadingTimeout: null,
            select12: {
                name: 'Australia',
                code: 'AU'
            },
            select12o5: '',
            select13: '',
            select14: 'Peach',
            colours,
            colourStrings,
            countries
        };
    },

    methods: {
        onQueryChange(query) {
            if (query.length === 0) {
                return;
            }

            this.fetchRemoteResults(query);
        },

        fetchRemoteResults: debounce(function (query) {
            this.select11Loading = true;

            if (this.select11LoadingTimeout) {
                clearTimeout(this.select11LoadingTimeout);
                this.select11LoadingTimeout = null;
            }

            this.select11LoadingTimeout = setTimeout(() => {
                query = query.toLowerCase();

                if (startsWith(query, 'red')) {
                    this.select11Options = redShades;
                    this.select11NoResults = false;
                } else if (startsWith(query, 'blue')) {
                    this.select11Options = blueShades;
                    this.select11NoResults = false;
                } else {
                    this.select11Options = [];
                    this.select11NoResults = true;
                }

                this.select11Loading = false;
                this.select11LoadingTimeout = null;
            }, 1200);
        }, 500)
    },
};
</script>

<style lang="scss">
@import '@/styles/imports';

.page--ui-select {
    .ui-select {
        margin-bottom: rem(32px);
        max-width: rem(400px);
    }
}
</style>
